using System;
using System.Collections.Generic;
using System.Diagnostics;
using Lucene.Net.Documents;
using Lucene.Net.Index;

namespace SpellCenter.Core
{
    static internal class FragmentMovementTracker 
    {
        public const string START_PFX = "start";
        public const string END_PFX = "end";
        public const string ID = "metaFileId";

//        private Document _doc;

        /// <summary>
        /// # of int place holder chars in the char offset. i.e. "startfile.name000005" for 6 (value 5)
        /// </summary>
        private const int IDFMT_LEN = 20; // Long enough to hold a full long int
        private const string IDFMT = "{0}{1}{2:00000000000000000000}";
        public static string GetStartName(string fileName, 
                                          long whereLuceneThinksItStarts)
        {
          return string.Format(IDFMT, 
                               START_PFX, 
                               fileName, 
                               whereLuceneThinksItStarts);
        }

        public static string GetEndName(string fileName, 
                                        long whereLuceneThinksItEnds) { 
          return string.Format(IDFMT, 
                               END_PFX, 
                               fileName, 
                               whereLuceneThinksItEnds); 
        }

        public static void DisAssembleString(string str, out string prefix, out string id, out int offset)
        {
            str = ExtractPrefix(out prefix, str);
            SplitOffsetAndId(str, out offset, out id);
        }

        private static void SplitOffsetAndId(string str, out int offset, out string id)
        {
            if(str.Length>=IDFMT_LEN)
            {
                string num = str.Substring(str.Length - IDFMT_LEN);
                if(!int.TryParse(num, out offset))
                    throw new InvalidOperationException("doesn't have correct identifier form: "+str);
                id = str.Substring(0, str.Length - IDFMT_LEN);
            }
            else throw new InvalidOperationException("offset too short: "+str);
        }

        private static string ExtractPrefix(out string prefix, string str)
        {
            if(str.StartsWith(START_PFX))
            {
                prefix = START_PFX;
                return str.Substring(START_PFX.Length);
            }
            else if(str.StartsWith(END_PFX))
            {
                prefix = END_PFX;
                return str.Substring(END_PFX.Length);
            }
            else throw new InvalidOperationException("Not a valid identifier: "+str);
        }

        private static int GetDocumentNumber(IndexReader reader)
        {
          TermDocs docs = reader.TermDocs(new Term(ID, ID));
          bool hasDoc = docs.Next();
          Debug.Assert(hasDoc);

          // There can be one and only one FragmentMovementTracker document, so return after the first
          int ret = docs.Doc();
          docs.Close();
          return ret;
        }

        public static Document GetDocument(IndexReader reader)
        {
          int docNum = GetDocumentNumber(reader);
          return reader.Document(docNum);
        }

        static private int GetCorrection(Fieldable field)
        {
            if (field == null)
                return 0;
            string value = field.StringValue();
            return int.Parse(value);
        }
        public static int GetStartCorrection(IndexReader reader, string fileName, long whereLuceneThinksItStarts)
        {
          Document document = GetDocument(reader);
            string fieldName= GetStartName(fileName,whereLuceneThinksItStarts);
            return GetCorrection(document.GetField(fieldName));
        }
        public static int GetEndCorrection(IndexReader reader, string fileName, long whereLuceneThinksItEnds)
        {
          Document document = GetDocument(reader);
          string fieldName = GetEndName(fileName, whereLuceneThinksItEnds);
            return GetCorrection(document.GetField(fieldName));
        }

      public static IEnumerable<Field> GetFields(IndexReader reader)
        {
          Document document = GetDocument(reader);
            foreach (Field field in document.Fields())
            {
                yield return field;
            }
        }
    }
}
